// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <pow.h>

#include <arith_uint256.h>
#include <chain.h>
#include <primitives/block.h>
#include <uint256.h>

//// 难度值计算
//// 我们要在一定时间后对难度进行调整，这里判断的就是判断是否需要对难度进行调整。
//获取下一次需要的工作量量
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); //工作量证明的限制值
    /// 每个难度周期仅进行一次周期的调整
    /// 检查是否到达难度调整时间，一个周期是指每两周进行一次调整，10分钟一块，2016块，总共20160分钟
    /// 块高是否是2016个区块的整数倍，如果不是就不用调整难度，就沿用用当前区块链顶点区块的难度值
    // Only change once per difficulty adjustment interval
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0) ///// 难度发生调整时才有所改变
    {
        if (params.fPowAllowMinDifficultyBlocks) //// 判断是否允许最小难度值
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            /**测试网络的特殊难度规则：
             * 如果新区块的时间戳超过两个区块的产生时间20min,那么返回限制挖矿难度
            */
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        return pindexLast->nBits; ////返回新区快的nBits值
    }

    /// 如果已经到了调整的时间，向上回溯找到这一组2016个区块的首地址
    // Go back by what we want to be 14 days worth of blocks
    int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
    assert(nHeightFirst >= 0);
    const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
    assert(pindexFirst);

    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); /////计算工作量证明
}

//// 关于计算难度的函数, 计算下一个难度值
/////计算工作量证明
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)  //如果不调整难度值
        return pindexLast->nBits;

    // Limit adjustment step
    /// 难度调整步骤, 难度调整的限制
    /// 如果生成时间小于1/4，就按照1/4来计算，大于4倍，就按照4倍来计算，以防止出现难度偏差过大的现象。
    /**
     * 这一部分的代码就是用来计算实际使用时间的代码，首先肯定是左后一块的时间减去第一块的时间，
     * 但接下来还有两个if判断语句，意思也很好理解，
     * 如果实际花费时间小于理论时间的1/4，就按照1/4来处理；
     * 如果实际花费时间大于理论时间的4倍，就按照4倍来处理。
     * 这个理由也很简单，如果相差的时间太大的话，那么理论上调整的难度的大小的范围就会很大，
     * 因此我们需要将其控制在一定的范围，那么在bitcoin中规定，最小不低于1/4，最大不超过4倍
     * */
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    /**计算调整后的难度值
     * 就可以计算出来新的难度值，那么我们计算后，还是刚才那个问题，一定不能忽视，就是难度越大，数值越小！
     * 因为我们如果nActualTimespan计算出来比20160(2016 * 10)分钟小的时候，最后得到的 bnNew是比之前要小的，
     * 但是我们想要的是比他大的值，因此我们一定要好好理解这个内容。
     * */
    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    arith_uint256 bnNew; /// 想要求的那个新的难度值
    bnNew.SetCompact(pindexLast->nBits); /// 首先将上一个区块的难度置到bnNew当中
    /// 新难度 = 旧难度 * 实际生成时间 / 预定生成时间
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;

    //// 难度也是有最低值的，起码不能比最低难度还容易
    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

////检验工作量证明
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;
    /// nBits就是当前的一个难度值，通过使用SetCompact函数将nBits转换为长度为256的类似于hash值的一个值
    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    /// 对这些内容进行检查，1）hash值为负数，2）bnTarget通过SetCompact获得的值为0，3）溢出，4）目标难度值比最低难度值还要低
    /**
     * 应该是 bnTarget大于powLimit（最低难度）的256转换值
     * bitcoin的大小是和数值正好相反的，也就是说，数值越大，难度越小！
     * */
    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
        return false;

    /// 如果所得hash值经过转换后比当前难度值的hash值要大，则返回失败
    /**
     * 判断的是最终类hash值与目标难度hash值的比较，这个比较还是按照我们正常的规定来理解就好，
     * 一旦找到比target小的值，就可以直接返回true，也就是找到了我们想要的那个nonce值。
     * */
    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}
